package com.protocols.sctp.m3ua.sccp.tcap;

import java.io.Serializable;
import java.math.BigInteger;

import javax.swing.text.StyledEditorKit.BoldAction;

import com.protocols.BinaryInteger;
import com.protocols.OctaveInteger;
import com.protocols.ProtoOperations;
import com.protocols.ProtocolStackException;
import com.protocols.sctp.m3ua.sccp.SccpUnitDataMsg;

public class TcapPrimitive7BitEncodedString extends TcapTag implements Serializable{


	
	private String TagValue="";
	private Boolean IsSet=false;
	public TcapPrimitive7BitEncodedString() {
		
	}
	
	public TcapPrimitive7BitEncodedString(int tag) {
		super(tag);
	}

	public byte [] getByteArray(){
		
		if (IsSet)
		{
			byte [] val = encode7bit(TagValue.getBytes());
			
			
			return ProtoOperations.concatArrays(super.getTagBytes(val.length),val);
		}
		
		return new byte[0];
		
		
	}


	public int getLength(){
		return getByteArray().length;
	}
	
	public int getContentLength(){
		return TagValue.getBytes().length;
	}

	public String getTagValue() {
		return TagValue;
	}


	public void setTagValue(String tagValue) {
		TagValue = tagValue;
		IsSet=true;
		
	}


	@Override
	public byte[] decode(byte[] input)  {
		
		if (input.length==0)
			return input;
		
		int tagcode= input[0] & 0b00011111;
		int tagclass=(input[0] & 0b11000000)>>6;
		int tagform=(input[0] & 0b00100000)>>5;
		int lengthstart=1;		// Start of the Length
		if (tagcode==31){
			if ((input[1]&0xff)<128){
				tagcode=input[1]&0xff;
				lengthstart=2;
			}
			else{
				
				tagcode=(input[1]<<7) |input[2];
				lengthstart=3;
			}
		}
		
		int taglength=input[lengthstart]& 0xff;
		int noflengthoctets=0;
		if (taglength>128){
			noflengthoctets = input[lengthstart] & 0b01111111;
			taglength= ProtoOperations.intFromByteArray(input, lengthstart+1, noflengthoctets);
		}
		else{
			taglength= input[lengthstart]&0xff;
		}
		byte remainder[]=input; 
		if (this.getTagClass()==tagclass && this.getTagForm()==tagform && this.getTagCode()==tagcode){
			
			int tagvaluestart=lengthstart+noflengthoctets+1;			
			
			byte[] tagval=ProtoOperations.extractByteArray(input, tagvaluestart, taglength);
			setTagValue( new String (decode(tagval)));		
			
			remainder=ProtoOperations.extractByteArray(input, tagvaluestart+taglength, input.length-(tagvaluestart+taglength));
		}
		return remainder;
		
	}
	
	public void setVisible(Boolean visible) {
		
		IsSet=visible;
	}
	
	public Boolean isVisible() {
		
		return IsSet;
	}
	
	private  byte[] decode7bit(byte[] input) {
		int fillBits=0;
		final int[] upperBits = {
		0xFE, // 0 = B7|B6|B5|B4|B3|B2|B1
		0xFC, // 1 = B7|B6|B5|B4|B3|B2
		0xF8, // 2 = B7|B6|B5|B4|B3
		0xF0, // 3 = B7|B6|B5|B4
		0xE0, // 4 = B7|B6|B5
		0xC0, // 5 = B7|B6
		0x80  // 6 = B7
		};    
		
		final int[] lowerBits = {
		0x01, // 0 =                   B0
		0x03, // 1 =                B1|B0
		0x07, // 2 =             B2|B1|B0
		0x0F, // 3 =          B3|B2|B1|B0
		0x1F, // 4 =       B4|B3|B2|B1|B0
		0x3F, // 5 =    B5|B4|B3|B2|B1|B0
		0x7F  // 6 = B6|B5|B4|B3|B2|B1|B0
		};    
		
		final int length = input.length;
		if (fillBits != 0) {
		final int len = length - 1;
		final int cut = lowerBits[fillBits - 1];
		final int move = 8 - fillBits;
		
			for (int f = 0; f < len; f++) {
			input[f] >>= fillBits;
			input[f] = (byte)((input[f + 1] & cut) << move);
			}
		input[len] >>= fillBits;
		}    
		
		int inputlength = length * 8 / 7;
		final byte[] output = new byte[inputlength];    
		
		int b = 6, p = 0;
		for (int i = 0; i < inputlength; i++) {
		switch (b) {
		case 7: // U0
		output[i] = (byte)((input[p] & upperBits[0]) >> 1);  
		break;        
		
		case 6: // L6
		output[i] = (byte)(input[p] & lowerBits[b]);
		break;
		
		default: // The rest
		output[i] =(byte)( ((input[p] & lowerBits[b]) << (6 - b))	+ ((input[p-1] & upperBits[b+1]) >> (b+2)));
		break;
		}
		
		if (--b == -1) b = 7;
		else p++;
		}
		
		return output;
	}
	
	
	private  byte[] encode7bit(byte[] input) {
		int fillBits=0;
	    final byte[] upperBits = {
	        (byte)0xFE, // 0 = B7|B6|B5|B4|B3|B2|B1
	        (byte)0xFC, // 1 = B7|B6|B5|B4|B3|B2
	        (byte)0xF8, // 2 = B7|B6|B5|B4|B3
	        (byte)0xF0, // 3 = B7|B6|B5|B4
	        (byte)0xE0, // 4 = B7|B6|B5
	        (byte)0xC0, // 5 = B7|B6
	        (byte)0x80  // 6 = B7
	    };
	    
	    final int[] lowerBits = {
	        0x01, // 0 =                   B0
	        0x03, // 1 =                B1|B0
	        0x07, // 2 =             B2|B1|B0
	        0x0F, // 3 =          B3|B2|B1|B0
	        0x1F, // 4 =       B4|B3|B2|B1|B0
	        0x3F, // 5 =    B5|B4|B3|B2|B1|B0
	        0x7F  // 6 = B6|B5|B4|B3|B2|B1|B0
	    };

	    final int inputlength = input.length;
	    final int ol = (inputlength * 7) / 8;
	    final boolean rest = ol != (double)inputlength * 7 / 8;
	    final boolean fill = fillBits != 0;
	    byte[] output = new byte[ol + (fill ? 1 : 0) + (rest ? 1 : 0)];

	    int b = 0, p = 0;
	    for (int i = 0; i < ol; i++) {
	      output[i] = (byte)((input[p] >> b)+ ((input[p + 1] & lowerBits[b]) << 7 - b));
	      if (++b == 7) {
	        b = 0;
	        p++;
	      }
	      p++;
	    }
	    if (rest) 
	    	output[ol] =(byte)( input[p] >> b);   

	    if (fill) {
	      final int cut = upperBits[7 - fillBits];
	      final int move = 8 - fillBits;
	      p = 0;
	      for (int i = 0; i <= ol + 1; i++) {
	        b = output[i] & cut;
	        output[i] ^= b;
	        output[i] <<= fillBits;
	        if (p != 0) output[i] = (byte)p;
	        p = b >> move;
	      }
	    }

	    return output;
	  }

}
